﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace AsyncListView
{
    public partial class AsyncListView : UserControl
    {
        delegate void lvUpdateProgress(string key, int progress);
        delegate void lvAddRow4(string key, string displayName, IconKey status, bool withProgressBar);
        delegate void lvAddRow3(string key, string displayName, IconKey status);
        delegate void lvAddRow(string key, string displayName);
        delegate void lvUpdateStatus3(string key, IconKey status, string msg);
        delegate void lvUpdateStatus4(string key, IconKey status, string msg, object longMsg);
        delegate void lvRemoveRow(string key, bool removeIfSuccess);

        public delegate void LinkClickHandler(object tag);
        public event LinkClickHandler ListViewLinkClick;
        public void OnListViewLinkClick(object tag)
        {
            if (ListViewLinkClick != null)
                ListViewLinkClick(tag);
        }

        private Font fntNormal;
        private Font fntLink;
        private Hashtable htLongMessage;
        private bool _AutoScroll = false;

        public enum IconKey
        {
            Processing = 0,
            Success = 1,
            Failure = 2,
            Waiting = 3,
            Information = 4
        };

        private string GetItemStatusText(IconKey status)
        {
            switch (status)
            {
                case IconKey.Failure:
                    return "Failure";
                case IconKey.Processing:
                    return "Processing";
                case IconKey.Success:
                    return "Success";
                case IconKey.Waiting:
                    return "Waiting";
                case IconKey.Information:
                    return "Information";
            }
            return "";
        }

        public AsyncListView()
        {
            InitializeComponent();
            fntNormal = lvMain.Font;
            fntLink = new Font(fntNormal, FontStyle.Underline);
            htLongMessage = new Hashtable();
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);

        }

        public ListView.ListViewItemCollection ListViewItems
        {
            get { return lvMain.Items; }
        }

        public int ItemCount
        {
            get { return lvMain.Items.Count; }
        }

        public new bool AutoScroll
        {
            get { return _AutoScroll; }
            set { _AutoScroll = value; }
        }


        public void AddRow(string key, string displayName)
        {
            if (lvMain.InvokeRequired)
            {
                lvMain.Invoke(new lvAddRow(AddRow), new object[] { key, displayName });
                return;
            }

            ListViewItem lvi = new ListViewItem("");
            lvi.ImageIndex = (int)IconKey.Waiting;
            lvi.Name = key;
            lvi.SubItems.Add(displayName);
            lvi.SubItems.Add(string.Empty);
            lvi.SubItems.Add(GetItemStatusText(IconKey.Waiting));
            lvMain.Items.Add(lvi);

            if (_AutoScroll)
                lvMain.EnsureVisible(lvMain.Items.Count - 1);
        }

        public void AddRow(string key, string displayName, IconKey status)
        {
            if (lvMain.InvokeRequired)
            {
                lvMain.Invoke(new lvAddRow3(AddRow), new object[] { key, displayName, status });
                return;
            }

            ListViewItem lvi = new ListViewItem("");
            lvi.ImageIndex = (int)IconKey.Waiting;
            lvi.Name = key;
            lvi.SubItems.Add(displayName);
            lvi.SubItems.Add(string.Empty);
            lvi.SubItems.Add(string.Empty);
            SetItemState(lvi, status, GetItemStatusText(status));
            lvMain.Items.Add(lvi);

            if (_AutoScroll)
                lvMain.EnsureVisible(lvMain.Items.Count - 1);
        }

        public void UpdateProgress(string key, int progress)
        {
            if (lvMain.InvokeRequired)
            {
                lvMain.Invoke(new lvUpdateProgress(UpdateProgress), new object[] { key, progress });
                return;
            }

            ListViewItem[] lvis = lvMain.Items.Find(key, true);
            if (lvis.Length == 1)
            {
                ProgressBar pb = lvMain.GetEmbeddedControl(2, lvis[0].Index) as ProgressBar;
                if (pb != null)
                {
                    if (progress < 0)
                    {
                        pb.Style = ProgressBarStyle.Marquee;
                        pb.Value = 50;
                    }
                    else
                    {
                        pb.Style = ProgressBarStyle.Continuous;
                        progress = Math.Min(pb.Maximum, progress);
                        progress = Math.Max(pb.Minimum, progress);
                        pb.Value = progress;
                    }
                }
                else
                {
                    lvis[0].SubItems[2].Text = progress.ToString();
                }
            }
        }

        public void AddRow(string key, string displayName, IconKey status, bool withProgressBar)
        {
            if (lvMain.InvokeRequired)
            {
                lvMain.Invoke(new lvAddRow4(AddRow), new object[] { key, displayName, status, withProgressBar });
                return;
            }

            ListViewItem lvi = new ListViewItem("");
            lvi.ImageIndex = (int)IconKey.Waiting;
            lvi.Name = key;
            lvi.SubItems.Add(displayName);
            lvi.SubItems.Add(string.Empty);
            lvi.SubItems.Add(string.Empty);
            SetItemState(lvi, status, GetItemStatusText(status));
            lvMain.Items.Add(lvi);
            if (withProgressBar)
            {
                ProgressBar pb = new ProgressBar();
                //pb.Dock = DockStyle.Fill;
                pb.Style = ProgressBarStyle.Continuous;
                pb.Value = 0;
                lvMain.AddEmbeddedControl(pb, 2, lvi.Index);
            }

            if (_AutoScroll)
                lvMain.EnsureVisible(lvMain.Items.Count - 1);
        }

        public void UpdateRowState(string key, IconKey status)
        {
            UpdateRowState(key, status, GetItemStatusText(status));
        }

        public void UpdateRowState(string key, IconKey status, string message)
        {
            if (lvMain.InvokeRequired)
            {
                lvMain.BeginInvoke(new lvUpdateStatus3(UpdateRowState), new object[] { key, status, message });
                return;
            }
            ListViewItem[] lvis = lvMain.Items.Find(key, true);
            if (lvis.Length == 1)
            {
                SetItemState(lvis[0], status, message);
            }
            //}
        }


        private void SetItemState(ListViewItem lvi, IconKey status, string message)
        {
            lvi.ImageIndex = (int)status;
            if (status != IconKey.Failure)
            {
                lvi.SubItems[3].ForeColor = Color.Black;
                lvi.SubItems[3].Text = message;
            }
            else
            {
                lvi.SubItems[3].BackColor = Color.FromArgb(255, 210, 210);
                lvi.SubItems[3].Text = message;
            }

        }

        public void UpdateRowState(string key, IconKey status, string message, object tag)
        {
            if (lvMain.InvokeRequired)
            {
                lvMain.Invoke(new lvUpdateStatus4(UpdateRowState), new object[] { key, status, message, tag });
                return;
            }
            ListViewItem[] lvis = lvMain.Items.Find(key, true);
            if (lvis.Length == 1)
            {
                SetItemState(lvis[0], status, message);

                if (tag != null)
                {
                    lvis[0].SubItems[3].Font = fntLink;
                    lvis[0].SubItems[3].ForeColor = Color.Blue;
                    lvis[0].SubItems[3].BackColor = Color.White;
                    lvis[0].SubItems[3].Tag = tag;
                    //htLongMessage[key] = longMessage;
                }
            }
            //}
        }

        public void RemoveRow(string key, bool removeIfSuccess)
        {
            if (lvMain.InvokeRequired)
            {
                lvMain.Invoke(new lvRemoveRow(RemoveRow), new object[] { key, removeIfSuccess });
                return;
            }
            ListViewItem[] lvis = lvMain.Items.Find(key, true);
            if (lvis.Length == 1)
            {
                if ((removeIfSuccess && lvis[0].ImageIndex == (int)IconKey.Success) || (!removeIfSuccess && lvis[0].ImageIndex != (int)IconKey.Success))
                    lvMain.Items.Remove(lvis[0]);
            }
        }

        private string GetFailureLongMessage(string key)
        {
            if (htLongMessage.ContainsKey(key))
            {
                return htLongMessage[key].ToString();
            }
            return GetItemStatusText(IconKey.Failure);
        }

        // ownerdraw code from http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.ownerdraw.aspx
        private void lvMain_DrawItem(object sender, DrawListViewItemEventArgs e)
        {
            if ((e.State & ListViewItemStates.Selected) != 0)
            {
                // Draw the background and focus rectangle for a selected item.
                e.Graphics.FillRectangle(Brushes.LightCyan, e.Bounds);
                
                //e.DrawFocusRectangle();
            }

            // Draw the item text for views other than the Details view.
            if (lvMain.View != View.Details)
            {
                e.DrawText();
            }

            Rectangle rectImage = new Rectangle((e.Item.SubItems[1].Bounds.Left - e.Item.Bounds.Left-16) / 2, e.Item.Bounds.Top, 16, 16);

            e.Graphics.DrawImage(e.Item.ImageList.Images[e.Item.ImageIndex], rectImage);
        }

        private void lvMain_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
        {
            TextFormatFlags flags = TextFormatFlags.Left;
            using (StringFormat sf = new StringFormat())
            {
                // Store the column text alignment, letting it default
                // to Left if it has not been set to Center or Right.
                switch (e.Header.TextAlign)
                {
                    case HorizontalAlignment.Center:
                        sf.Alignment = StringAlignment.Center;
                        flags = TextFormatFlags.HorizontalCenter;
                        break;
                    case HorizontalAlignment.Right:
                        sf.Alignment = StringAlignment.Far;
                        flags = TextFormatFlags.Right;
                        break;
                }
                e.DrawText(flags);
            }            
        }

        private void lvMain_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
        {
            e.DrawBackground();
            e.DrawText();            
            return;           
        }

        private void lvMain_MouseClick(object sender, MouseEventArgs e)
        {
            ListViewHitTestInfo lvHitTestInfo = lvMain.HitTest(e.Location);
            if (lvHitTestInfo.SubItem != null && lvHitTestInfo.SubItem.Tag != null)
            {
                OnListViewLinkClick(lvHitTestInfo.SubItem.Tag);
            }            
        }       

    }
}
